package main

import (
	"fmt"
	"net"
	"os"
	"os/signal"
	"syscall"
	"time"

	g "github.com/gosnmp/gosnmp"
)

// resultSummary 结果统计
var resultSummary = struct {
	StartTime         time.Time
	MinRTT            time.Duration
	MaxRTT            time.Duration
	TotalRTT          time.Duration
	LoopCount         int
	TotalTokenCount   int
	SuccessTokenCount int
}{
	StartTime: time.Now(),
	MinRTT:    -1,
}

// init 初始化日志记录和加载配置
func init() {
	mustSetLogging()
	log.Info("程序启动，开始加载配置...")

	if err := loadConfig(); err != nil {
		log.Critical(err.Error())
		os.Exit(1)
	}
	log.Info("已加载配置")
}

func main() {
	log.Debug("SNMP 连接...")
	snmp, err := snmpConnect()
	if err != nil {
		log.Error("SNMP连接失败：%s", err.Error())
	}
	defer snmp.Conn.Close()

	quitCh := make(chan bool)
	listenQuitSignal(quitCh)

	for numOfOIDs := len(config.OIDs); ; {
		select {
		case <-quitCh:
			fmt.Println("============================== 总结 ==============================")
			fmt.Printf("共测试 %d 轮，总耗时：%s\n", resultSummary.LoopCount, time.Now().Sub(resultSummary.StartTime))
			fmt.Printf("共发送 SNMPGET 请求 %d 次，成功 %d 次\n", resultSummary.TotalTokenCount, resultSummary.SuccessTokenCount)
			if resultSummary.SuccessTokenCount > 0 {
				fmt.Printf("往返最大/最小/平均时延：%s / %s / %s\n", resultSummary.MaxRTT, resultSummary.MinRTT,
					resultSummary.TotalRTT/time.Duration(resultSummary.SuccessTokenCount),
				)
			}
			fmt.Println("==================================================================")
			return
		default:
			resultSummary.LoopCount++
			log.Info("==================== 第 %04d 轮 ====================", resultSummary.LoopCount)
			for i := 0; i <= numOfOIDs/config.MaxTokenNumber; i++ {
				sta, end := i*config.MaxTokenNumber, i*config.MaxTokenNumber+config.MaxTokenNumber
				if sta == numOfOIDs {
					break
				} else if end > numOfOIDs {
					end = numOfOIDs
				}

				log.Debug("发送 SNMPGET 请求，OIDs: %v", config.OIDs[sta:end])
				resultSummary.TotalTokenCount++

				st := time.Now()
				result, err := snmp.Get(config.OIDs[sta:end])
				rtt := time.Now().Sub(st)
				if err != nil {
					log.Error("SNMPGET 失败: %s", err.Error())
					continue
				}
				log.Debug("往返时延：%s", rtt.String())

				resultSummary.SuccessTokenCount++
				resultSummary.TotalRTT += rtt
				switch {
				case resultSummary.MinRTT < 0 || resultSummary.MinRTT > rtt:
					resultSummary.MinRTT = rtt
				case resultSummary.MaxRTT < rtt:
					resultSummary.MaxRTT = rtt
				}

				for _, v := range result.Variables {
					switch v.Type {
					case g.OctetString:
						bs, _ := v.Value.(string)
						if len(bs) == 6 {
							macStr := net.HardwareAddr(bs).String()
							if _, err := net.ParseMAC(macStr); err == nil {
								log.Info("%-30s = %20s  %s", v.Name, "PhysAddress", macStr)
								continue
							}
						}
						log.Info("%-30s = %20s  %s", v.Name, v.Type, v.Value)

					default:
						log.Info("%-30s = %20s  %v", v.Name, v.Type, v.Value)
					}
				}
				time.Sleep(5 * time.Millisecond)
			}
			time.Sleep(time.Duration(config.IntervalMs) * time.Millisecond)
		}
	}
}

// listenQuitSignal 监听进程信号
func listenQuitSignal(ch chan<- bool) {
	sigCh := make(chan os.Signal, 1)
	signal.Notify(sigCh, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)

	go func() {
		for sig := range sigCh {
			log.Info("收到一个退出进程信号: %s，程序将有序的退出", sig.String())
			ch <- true
			return
		}
	}()
}
